"""
重试服务模块
提供异步操作的重试机制，提高系统的稳定性
支持指数退避策略，减少服务压力
"""

import asyncio
import logging
from typing import Callable, Any, Optional, Type, Union, Tuple
from functools import wraps

logger = logging.getLogger(__name__)


def async_retry(
    retries: int = 3,
    delay: float = 1.0,
    backoff: float = 2.0,
    exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = Exception,
    max_delay: float = 60.0
):
    """
    异步重试装饰器
    支持指数退避和最大延迟限制
    
    Args:
        retries: 最大重试次数
        delay: 初始延迟时间（秒）
        backoff: 退避因子（每次重试延迟时间乘以此因子）
        exceptions: 需要重试的异常类型
        max_delay: 最大延迟时间（秒）
        
    Returns:
        装饰器函数
    """
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        async def wrapper(*args, **kwargs) -> Any:
            last_exception = None
            current_delay = delay
            
            for attempt in range(retries + 1):  # +1 因为包含初始尝试
                try:
                    # 尝试执行函数
                    result = await func(*args, **kwargs)
                    
                    # 如果成功且不是第一次尝试，记录恢复信息
                    if attempt > 0:
                        logger.info(f"{func.__name__} 在第 {attempt + 1} 次尝试后成功执行")
                    
                    return result
                    
                except exceptions as e:
                    last_exception = e
                    
                    # 如果是最后一次尝试，不再重试
                    if attempt == retries:
                        logger.error(f"{func.__name__} 在 {retries + 1} 次尝试后仍然失败: {e}")
                        break
                    
                    # 记录重试信息
                    logger.warning(f"{func.__name__} 第 {attempt + 1} 次尝试失败: {e}，{current_delay:.1f}秒后重试")
                    
                    # 等待重试
                    await asyncio.sleep(current_delay)
                    
                    # 计算下次延迟时间（指数退避）
                    current_delay = min(current_delay * backoff, max_delay)
            
            # 如果所有重试都失败，抛出最后一个异常
            raise last_exception
            
        return wrapper
    return decorator


class RetryConfig:
    """重试配置类"""
    
    def __init__(
        self,
        retries: int = 3,
        delay: float = 1.0,
        backoff: float = 2.0,
        max_delay: float = 60.0,
        exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = Exception
    ):
        self.retries = retries
        self.delay = delay
        self.backoff = backoff
        self.max_delay = max_delay
        self.exceptions = exceptions
    
    def to_decorator(self):
        """转换为装饰器"""
        return async_retry(
            retries=self.retries,
            delay=self.delay,
            backoff=self.backoff,
            exceptions=self.exceptions,
            max_delay=self.max_delay
        )


# 预定义的重试配置
NETWORK_RETRY_CONFIG = RetryConfig(
    retries=3,
    delay=1.0,
    backoff=2.0,
    max_delay=30.0,
    exceptions=(ConnectionError, TimeoutError, OSError)
)

API_RETRY_CONFIG = RetryConfig(
    retries=2,
    delay=0.5,
    backoff=2.0,
    max_delay=10.0,
    exceptions=(ConnectionError, TimeoutError)
)

DATABASE_RETRY_CONFIG = RetryConfig(
    retries=3,
    delay=0.1,
    backoff=1.5,
    max_delay=5.0,
    exceptions=(ConnectionError, TimeoutError)
)


async def retry_async_operation(
    operation: Callable,
    *args,
    retries: int = 3,
    delay: float = 1.0,
    backoff: float = 2.0,
    exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = Exception,
    max_delay: float = 60.0,
    **kwargs
) -> Any:
    """
    直接重试异步操作的函数版本
    适用于不使用装饰器的场景
    
    Args:
        operation: 要执行的异步操作
        *args: 传递给操作的位置参数
        retries: 最大重试次数
        delay: 初始延迟时间
        backoff: 退避因子
        exceptions: 需要重试的异常类型
        max_delay: 最大延迟时间
        **kwargs: 传递给操作的关键字参数
        
    Returns:
        操作的返回结果
        
    Raises:
        最后一次尝试的异常
    """
    last_exception = None
    current_delay = delay
    
    for attempt in range(retries + 1):
        try:
            result = await operation(*args, **kwargs)
            
            if attempt > 0:
                logger.info(f"操作在第 {attempt + 1} 次尝试后成功执行")
            
            return result
            
        except exceptions as e:
            last_exception = e
            
            if attempt == retries:
                logger.error(f"操作在 {retries + 1} 次尝试后仍然失败: {e}")
                break
            
            logger.warning(f"操作第 {attempt + 1} 次尝试失败: {e}，{current_delay:.1f}秒后重试")
            
            await asyncio.sleep(current_delay)
            current_delay = min(current_delay * backoff, max_delay)
    
    raise last_exception


class RetryManager:
    """重试管理器，统一管理重试策略"""
    
    def __init__(self):
        self.configs = {
            'network': NETWORK_RETRY_CONFIG,
            'api': API_RETRY_CONFIG,
            'database': DATABASE_RETRY_CONFIG
        }
    
    def add_config(self, name: str, config: RetryConfig):
        """添加重试配置"""
        self.configs[name] = config
    
    def get_config(self, name: str) -> Optional[RetryConfig]:
        """获取重试配置"""
        return self.configs.get(name)
    
    def retry_with_config(self, config_name: str):
        """使用指定配置的重试装饰器"""
        config = self.get_config(config_name)
        if not config:
            raise ValueError(f"未找到重试配置: {config_name}")
        
        return config.to_decorator()
    
    async def execute_with_retry(
        self,
        operation: Callable,
        config_name: str,
        *args,
        **kwargs
    ) -> Any:
        """使用指定配置执行重试操作"""
        config = self.get_config(config_name)
        if not config:
            raise ValueError(f"未找到重试配置: {config_name}")
        
        return await retry_async_operation(
            operation,
            *args,
            retries=config.retries,
            delay=config.delay,
            backoff=config.backoff,
            exceptions=config.exceptions,
            max_delay=config.max_delay,
            **kwargs
        )


# 全局重试管理器实例
retry_manager = RetryManager() 